/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package java.io;

import elemental2.dom.DomGlobal;
import elemental2.webstorage.Storage;
import elemental2.webstorage.WebStorageWindow;
import java.util.ArrayList;

/**
 * LocalStorage based File implementation for GWT. Should probably have used Harmony as a starting
 * point instead of writing this from scratch.
 *
 * @author Stefan Haustein
 */
public class File {

  public static final File ROOT = new File("");

  public static final char separatorChar = '/';

  public static final String separator = "" + separatorChar;

  public static final char pathSeparatorChar = ':';

  public static final String pathSeparator = "" + pathSeparatorChar;

  public static final Storage LocalStorage = WebStorageWindow.of(DomGlobal.window).localStorage;

  File parent;
  String name;
  boolean absolute;

  public File(String pathname) {
    while (pathname.endsWith(separator) && pathname.length() > 0) {
      pathname = pathname.substring(0, pathname.length() - 1);
    }

    int cut = pathname.lastIndexOf(separatorChar);
    if (cut == -1) {
      name = pathname;
    } else if (cut == 0) {
      name = pathname.substring(cut);
      parent = name.equals("") ? null : ROOT;
    } else {
      name = pathname.substring(cut + 1);
      parent = new File(pathname.substring(0, cut));
    }

    //	  Compatibility.println("new File ('"+pathname+ "'); canonical name: '" + getCanonicalPath()
    // + "'");
  }

  public File(String parent, String child) {
    this(new File(parent), child);
  }

  public File(File parent, String child) {
    this.parent = parent;
    this.name = child;
  }

  /*
  public File(URI uri) {
  }
  */

  public static File[] listRoots() {
    return new File[] {ROOT};
  }

  public static File createTempFile(String prefix, String suffix, File directory)
      throws IOException {
    throw new RuntimeException("NYI: createTempFile");
  }

  public static File createTempFile(String prefix, String suffix) throws IOException {
    throw new RuntimeException("NYI: createTempFile");
  }

  public String getName() {
    return name;
  }

  public String getParent() {
    return parent == null ? "" : parent.getPath();
  }

  public File getParentFile() {
    return parent;
  }

  public String getAbsolutePath() {
    String path = getAbsoluteFile().getPath();
    return path.length() == 0 ? "/" : path;
  }

  public File getAbsoluteFile() {
    if (isAbsolute()) {
      return this;
    }
    if (parent == null) {
      return new File(ROOT, name);
    }
    return new File(parent.getAbsoluteFile(), name);
  }

  public boolean isAbsolute() {
    if (isRoot()) {
      return true;
    }
    if (parent == null) {
      return false;
    }
    return parent.isAbsolute();
  }

  private boolean isRoot() {
    return name.equals("") && parent == null;
  }

  /*
  public URL toURL() throws MalformedURLException {
  }

  public URI toURI() {
  }
  */

  public boolean canRead() {
    return true;
  }

  public boolean canWrite() {
    return true;
  }

  public boolean isDirectory() {
    String s = LocalStorage.getItem(getCanonicalPath());
    return s != null && s.startsWith("{");
  }

  public String getCanonicalPath() {
    return getCanonicalFile().getAbsolutePath();
  }

  public File getCanonicalFile() {
    File cParent = parent == null ? null : parent.getCanonicalFile();
    if (name.equals(".")) {
      return cParent == null ? ROOT : cParent;
    }
    if (cParent != null && cParent.name.equals("")) {
      cParent = null;
    }
    if (name.equals("..")) {
      if (cParent == null) {
        return ROOT;
      }
      if (cParent.parent == null) {
        return ROOT;
      }
      return cParent.parent;
    }
    if (cParent == null && !name.equals("")) {
      return new File(ROOT, name);
    }
    return new File(cParent, name);
  }

  public boolean isFile() {
    String s = LocalStorage.getItem(getCanonicalPath());
    return s != null && !s.startsWith("{");
  }

  public boolean isHidden() {
    return false;
  }

  public long lastModified() {
    return 0;
  }

  public long length() {
    try {
      if (!exists()) {
        return 0;
      }

      RandomAccessFile raf = new RandomAccessFile(this, "r");
      long len = raf.length();
      raf.close();
      return len;
    } catch (IOException e) {
      return 0;
    }
  }

  public boolean exists() {
    return LocalStorage.getItem(getCanonicalPath()) != null;
  }

  public boolean createNewFile() throws IOException {
    if (exists()) {
      return false;
    }
    if (!parent.exists()) {
      return false;
    }
    LocalStorage.setItem(getCanonicalPath(), RandomAccessFile.btoa(""));
    return true;
  }

  public boolean delete() {
    if (!exists()) {
      return false;
    }
    LocalStorage.removeItem(getCanonicalPath());
    return true;
  }

  /*
  public String[] list(FilenameFilter filter) {
    return null;
  }
  */

  public void deleteOnExit() {
    throw new RuntimeException("NYI: File.deleteOnExit()");
  }

  public String[] list() {
    throw new RuntimeException("NYI: File.list()");
  }

  /*
  public File[] listFiles(FileFilter filter) {
    return null;
  }
  */

  public File[] listFiles() {
    return listFiles(null);
  }

  public File[] listFiles(FilenameFilter filter) {
    ArrayList<File> files = new ArrayList<File>();
    String prefix = getCanonicalPath();
    if (!prefix.endsWith(separator)) {
      prefix += separatorChar;
    }

    int cut = prefix.length();
    int cnt = LocalStorage.getLength();
    for (int i = 0; i < cnt; i++) {
      String key = LocalStorage.key(i);
      if (key.startsWith(prefix) && key.indexOf(separatorChar, cut) == -1) {
        String name = key.substring(cut);
        if (filter == null || filter.accept(this, name)) {
          files.add(new File(this, name));
        }
      }
    }
    return files.toArray(new File[files.size()]);
  }

  public boolean mkdir() {
    if (parent != null && !parent.exists()) {
      return false;
    }
    if (exists()) {
      return false;
    }
    // We may want to make this a JS map
    LocalStorage.setItem(getCanonicalPath(), "{}");
    return true;
  }

  public boolean mkdirs() {
    if (parent != null) {
      parent.mkdirs();
    }
    return mkdir();
  }

  public boolean renameTo(File dest) {
    throw new RuntimeException("renameTo()");
  }

  public boolean setLastModified(long time) {
    return false;
  }

  public boolean setReadOnly() {
    return false;
  }

  public int compareTo(File pathname) {
    throw new RuntimeException("NYI: File.compareTo()");
  }

  public int hashCode() {
    return parent != null ? parent.hashCode() + name.hashCode() : name.hashCode();
  }

  public boolean equals(Object obj) {
    if (!(obj instanceof File)) {
      return false;
    }
    return getPath().equals(((File) obj).getPath());
  }

  public String getPath() {
    return parent == null ? name : (parent.getPath() + separatorChar + name);
  }

  public String toString() {
    return name;
  }
}
